package org.apache.maven.repository.legacy.resolver;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.metadata.ResolutionGroup;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.CyclicDependencyException;
import org.apache.maven.artifact.resolver.ResolutionListener;
import org.apache.maven.artifact.resolver.ResolutionListenerForDepMgmt;
import org.apache.maven.artifact.resolver.ResolutionNode;
import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.ManagedVersionMap;
import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.LegacySupport;
import org.apache.maven.repository.legacy.metadata.ArtifactMetadataRetrievalException;
import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest;
import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest;
import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;

/**
 * @author <a href="mailto:brett@apache.org">Brett Porter</a>
 * @author Jason van Zyl
 */
@Component(role = LegacyArtifactCollector.class)
public class DefaultLegacyArtifactCollector implements LegacyArtifactCollector {
  @Requirement(hint = "nearest")
  private ConflictResolver defaultConflictResolver;

  @Requirement
  private Logger logger;

  @Requirement
  private LegacySupport legacySupport;

  private void injectSession(ArtifactResolutionRequest request) {
    MavenSession session = legacySupport.getSession();

    if (session != null) {
      request.setOffline(session.isOffline());
      request.setForceUpdate(session.getRequest().isUpdateSnapshots());
      request.setServers(session.getRequest().getServers());
      request.setMirrors(session.getRequest().getMirrors());
      request.setProxies(session.getRequest().getProxies());
    }
  }

  public ArtifactResolutionResult collect(Set<Artifact> artifacts, Artifact originatingArtifact, Map managedVersions,
      ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories, ArtifactMetadataSource source,
      ArtifactFilter filter, List<ResolutionListener> listeners, List<ConflictResolver> conflictResolvers) {
    ArtifactResolutionRequest request = new ArtifactResolutionRequest();
    request.setLocalRepository(localRepository);
    request.setRemoteRepositories(remoteRepositories);
    injectSession(request);
    return collect(artifacts, originatingArtifact, managedVersions, request, source, filter, listeners,
        conflictResolvers);
  }

  public ArtifactResolutionResult collect(Set<Artifact> artifacts, Artifact originatingArtifact, Map managedVersions,
      ArtifactResolutionRequest repositoryRequest, ArtifactMetadataSource source, ArtifactFilter filter,
      List<ResolutionListener> listeners, List<ConflictResolver> conflictResolvers) {
    ArtifactResolutionResult result = new ArtifactResolutionResult();

    result.setOriginatingArtifact(originatingArtifact);

    if (conflictResolvers == null) {
      conflictResolvers = Collections.singletonList(defaultConflictResolver);
    }

    Map<Object, List<ResolutionNode>> resolvedArtifacts = new LinkedHashMap<Object, List<ResolutionNode>>();

    ResolutionNode root = new ResolutionNode(originatingArtifact, repositoryRequest.getRemoteRepositories());

    try {
      root.addDependencies(artifacts, repositoryRequest.getRemoteRepositories(), filter);
    } catch (CyclicDependencyException e) {
      result.addCircularDependencyException(e);

      return result;
    } catch (OverConstrainedVersionException e) {
      result.addVersionRangeViolation(e);

      return result;
    }

    ManagedVersionMap versionMap = getManagedVersionsMap(originatingArtifact, managedVersions);

    try {
      recurse(result, root, resolvedArtifacts, versionMap, repositoryRequest, source, filter, listeners,
          conflictResolvers);
    } catch (CyclicDependencyException e) {
      logger.debug("While recursing: " + e.getMessage(), e);
      result.addCircularDependencyException(e);
    } catch (OverConstrainedVersionException e) {
      logger.debug("While recursing: " + e.getMessage(), e);
      result.addVersionRangeViolation(e);
    } catch (ArtifactResolutionException e) {
      logger.debug("While recursing: " + e.getMessage(), e);
      result.addErrorArtifactException(e);
    }

    Set<ResolutionNode> set = new LinkedHashSet<ResolutionNode>();

    for (List<ResolutionNode> nodes : resolvedArtifacts.values()) {
      for (ResolutionNode node : nodes) {
        if (!node.equals(root) && node.isActive()) {
          Artifact artifact = node.getArtifact();

          try {
            if (node.filterTrail(filter)) {
              // If it was optional and not a direct dependency,
              // we don't add it or its children, just allow the update of the version and
              // artifactScope
              if (node.isChildOfRootNode() || !artifact.isOptional()) {
                artifact.setDependencyTrail(node.getDependencyTrail());

                set.add(node);

                // This is required right now.
                result.addArtifact(artifact);
              }
            }
          } catch (OverConstrainedVersionException e) {
            result.addVersionRangeViolation(e);
          }
        }
      }
    }

    result.setArtifactResolutionNodes(set);

    return result;
  }

  /**
   * Get the map of managed versions, removing the originating artifact if it is also in managed
   * versions
   *
   * @param originatingArtifact artifact we are processing
   * @param managedVersions original managed versions
   */
  private ManagedVersionMap getManagedVersionsMap(Artifact originatingArtifact, Map managedVersions) {
    ManagedVersionMap versionMap;
    if ((managedVersions != null) && (managedVersions instanceof ManagedVersionMap)) {
      versionMap = (ManagedVersionMap) managedVersions;
    } else {
      versionMap = new ManagedVersionMap(managedVersions);
    }

    /*
     * remove the originating artifact if it is also in managed versions to avoid being modified
     * during resolution
     */
    Artifact managedOriginatingArtifact = versionMap.get(originatingArtifact.getDependencyConflictId());

    if (managedOriginatingArtifact != null) {
      // TODO we probably want to warn the user that he is building an artifact with
      // different values than in dependencyManagement
      if (managedVersions instanceof ManagedVersionMap) {
        /* avoid modifying the managedVersions parameter creating a new map */
        versionMap = new ManagedVersionMap(managedVersions);
      }
      versionMap.remove(originatingArtifact.getDependencyConflictId());
    }

    return versionMap;
  }

  private void recurse(ArtifactResolutionResult result, ResolutionNode node,
      Map<Object, List<ResolutionNode>> resolvedArtifacts, ManagedVersionMap managedVersions,
      ArtifactResolutionRequest request, ArtifactMetadataSource source, ArtifactFilter filter,
      List<ResolutionListener> listeners, List<ConflictResolver> conflictResolvers) throws ArtifactResolutionException {
    fireEvent(ResolutionListener.TEST_ARTIFACT, listeners, node);

    Object key = node.getKey();

    // TODO: Does this check need to happen here? Had to add the same call
    // below when we iterate on child nodes -- will that suffice?
    if (managedVersions.containsKey(key)) {
      manageArtifact(node, managedVersions, listeners);
    }

    List<ResolutionNode> previousNodes = resolvedArtifacts.get(key);

    if (previousNodes != null) {
      for (ResolutionNode previous : previousNodes) {
        try {
          if (previous.isActive()) {
            // Version mediation
            VersionRange previousRange = previous.getArtifact().getVersionRange();
            VersionRange currentRange = node.getArtifact().getVersionRange();

            if ((previousRange != null) && (currentRange != null)) {
              // TODO: shouldn't need to double up on this work, only done for simplicity of
              // handling
              // recommended
              // version but the restriction is identical
              VersionRange newRange = previousRange.restrict(currentRange);
              // TODO: ick. this forces the OCE that should have come from the previous call. It is
              // still
              // correct
              if (newRange.isSelectedVersionKnown(previous.getArtifact())) {
                fireEvent(ResolutionListener.RESTRICT_RANGE, listeners, node, previous.getArtifact(), newRange);
              }
              previous.getArtifact().setVersionRange(newRange);
              node.getArtifact().setVersionRange(currentRange.restrict(previousRange));

              // Select an appropriate available version from the (now restricted) range
              // Note this version was selected before to get the appropriate POM
              // But it was reset by the call to setVersionRange on restricting the version
              ResolutionNode[] resetNodes = { previous, node };
              for (int j = 0; j < 2; j++) {
                Artifact resetArtifact = resetNodes[j].getArtifact();

                // MNG-2123: if the previous node was not a range, then it wouldn't have any
                // available
                // versions. We just clobbered the selected version above. (why? i have no idea.)
                // So since we are here and this is ranges we must go figure out the version (for a
                // third time...)
                if (resetArtifact.getVersion() == null && resetArtifact.getVersionRange() != null) {

                  // go find the version. This is a total hack. See previous comment.
                  List<ArtifactVersion> versions = resetArtifact.getAvailableVersions();
                  if (versions == null) {
                    try {
                      MetadataResolutionRequest metadataRequest = new DefaultMetadataResolutionRequest(request);
                      metadataRequest.setArtifact(resetArtifact);
                      versions = source.retrieveAvailableVersions(metadataRequest);
                      resetArtifact.setAvailableVersions(versions);
                    } catch (ArtifactMetadataRetrievalException e) {
                      resetArtifact.setDependencyTrail(node.getDependencyTrail());
                      throw new ArtifactResolutionException("Unable to get dependency information: " + e.getMessage(),
                          resetArtifact, request.getRemoteRepositories(), e);
                    }
                  }
                  // end hack

                  // MNG-2861: match version can return null
                  ArtifactVersion selectedVersion = resetArtifact.getVersionRange()
                      .matchVersion(resetArtifact.getAvailableVersions());
                  if (selectedVersion != null) {
                    resetArtifact.selectVersion(selectedVersion.toString());
                  } else {
                    throw new OverConstrainedVersionException(
                        " Unable to find a version in " + resetArtifact.getAvailableVersions() + " to match the range "
                            + resetArtifact.getVersionRange(),
                        resetArtifact);
                  }

                  fireEvent(ResolutionListener.SELECT_VERSION_FROM_RANGE, listeners, resetNodes[j]);
                }
              }
            }

            // Conflict Resolution
            ResolutionNode resolved = null;
            for (Iterator j = conflictResolvers.iterator(); (resolved == null) && j.hasNext();) {
              ConflictResolver conflictResolver = (ConflictResolver) j.next();

              resolved = conflictResolver.resolveConflict(previous, node);
            }

            if (resolved == null) {
              // TODO: add better exception that can detail the two conflicting artifacts
              ArtifactResolutionException are = new ArtifactResolutionException(
                  "Cannot resolve artifact version conflict between " + previous.getArtifact().getVersion() + " and "
                      + node.getArtifact().getVersion(),
                  previous.getArtifact());
              result.addVersionRangeViolation(are);
            }

            if ((resolved != previous) && (resolved != node)) {
              // TODO: add better exception
              result.addVersionRangeViolation(new ArtifactResolutionException(
                  "Conflict resolver returned unknown resolution node: ", resolved.getArtifact()));
            }

            // TODO: should this be part of mediation?
            // previous one is more dominant
            ResolutionNode nearest;
            ResolutionNode farthest;

            if (resolved == previous) {
              nearest = previous;
              farthest = node;
            } else {
              nearest = node;
              farthest = previous;
            }

            if (checkScopeUpdate(farthest, nearest, listeners)) {
              // if we need to update artifactScope of nearest to use farthest artifactScope, use
              // the
              // nearest version, but farthest artifactScope
              nearest.disable();
              farthest.getArtifact().setVersion(nearest.getArtifact().getVersion());
              fireEvent(ResolutionListener.OMIT_FOR_NEARER, listeners, nearest, farthest.getArtifact());
            } else {
              farthest.disable();
              fireEvent(ResolutionListener.OMIT_FOR_NEARER, listeners, farthest, nearest.getArtifact());
            }
          }
        } catch (OverConstrainedVersionException e) {
          result.addVersionRangeViolation(e);
        }
      }
    } else {
      previousNodes = new ArrayList<ResolutionNode>();

      resolvedArtifacts.put(key, previousNodes);
    }
    previousNodes.add(node);

    if (node.isActive()) {
      fireEvent(ResolutionListener.INCLUDE_ARTIFACT, listeners, node);
    }

    // don't pull in the transitive deps of a system-scoped dependency.
    if (node.isActive() && !Artifact.SCOPE_SYSTEM.equals(node.getArtifact().getScope())) {
      fireEvent(ResolutionListener.PROCESS_CHILDREN, listeners, node);

      Artifact parentArtifact = node.getArtifact();

      for (Iterator i = node.getChildrenIterator(); i.hasNext();) {
        ResolutionNode child = (ResolutionNode) i.next();

        try {

          // We leave in optional ones, but don't pick up its dependencies
          if (!child.isResolved() && (!child.getArtifact().isOptional() || child.isChildOfRootNode())) {
            Artifact artifact = child.getArtifact();
            artifact.setDependencyTrail(node.getDependencyTrail());
            List<ArtifactRepository> childRemoteRepositories = child.getRemoteRepositories();

            MetadataResolutionRequest metadataRequest = new DefaultMetadataResolutionRequest(request);
            metadataRequest.setArtifact(artifact);
            metadataRequest.setRemoteRepositories(childRemoteRepositories);

            try {
              ResolutionGroup rGroup;

              Object childKey;
              do {
                childKey = child.getKey();

                if (managedVersions.containsKey(childKey)) {
                  // If this child node is a managed dependency, ensure
                  // we are using the dependency management version
                  // of this child if applicable b/c we want to use the
                  // managed version's POM, *not* any other version's POM.
                  // We retrieve the POM below in the retrieval step.
                  manageArtifact(child, managedVersions, listeners);

                  // Also, we need to ensure that any exclusions it presents are
                  // added to the artifact before we retrive the metadata
                  // for the artifact; otherwise we may end up with unwanted
                  // dependencies.
                  Artifact ma = managedVersions.get(childKey);
                  ArtifactFilter managedExclusionFilter = ma.getDependencyFilter();
                  if (null != managedExclusionFilter) {
                    if (null != artifact.getDependencyFilter()) {
                      AndArtifactFilter aaf = new AndArtifactFilter();
                      aaf.add(artifact.getDependencyFilter());
                      aaf.add(managedExclusionFilter);
                      artifact.setDependencyFilter(aaf);
                    } else {
                      artifact.setDependencyFilter(managedExclusionFilter);
                    }
                  }
                }

                if (artifact.getVersion() == null) {
                  // set the recommended version
                  // TODO: maybe its better to just pass the range through to retrieval and use a
                  // transformation?
                  ArtifactVersion version;
                  if (!artifact.isSelectedVersionKnown()) {
                    List<ArtifactVersion> versions = artifact.getAvailableVersions();
                    if (versions == null) {
                      versions = source.retrieveAvailableVersions(metadataRequest);
                      artifact.setAvailableVersions(versions);
                    }

                    Collections.sort(versions);

                    VersionRange versionRange = artifact.getVersionRange();

                    version = versionRange.matchVersion(versions);

                    if (version == null) {
                      if (versions.isEmpty()) {
                        throw new OverConstrainedVersionException(
                            "No versions are present in the repository for the artifact with a range " + versionRange,
                            artifact, childRemoteRepositories);
                      }

                      throw new OverConstrainedVersionException(
                          "Couldn't find a version in " + versions + " to match range " + versionRange, artifact,
                          childRemoteRepositories);
                    }
                  } else {
                    version = artifact.getSelectedVersion();
                  }

                  artifact.selectVersion(version.toString());
                  fireEvent(ResolutionListener.SELECT_VERSION_FROM_RANGE, listeners, child);
                }

                rGroup = source.retrieve(metadataRequest);

                if (rGroup == null) {
                  break;
                }
              } while (!childKey.equals(child.getKey()));

              if (parentArtifact != null && parentArtifact.getDependencyFilter() != null
                  && !parentArtifact.getDependencyFilter().include(artifact)) {
                // MNG-3769: the [probably relocated] artifact is excluded.
                // We could process exclusions on relocated artifact details in the
                // MavenMetadataSource.createArtifacts(..) step, BUT that would
                // require resolving the POM from the repository very early on in
                // the build.
                continue;
              }

              // TODO might be better to have source.retrieve() throw a specific exception for this
              // situation
              // and catch here rather than have it return null
              if (rGroup == null) {
                // relocated dependency artifact is declared excluded, no need to add and recurse
                // further
                continue;
              }

              child.addDependencies(rGroup.getArtifacts(), rGroup.getResolutionRepositories(), filter);

            } catch (CyclicDependencyException e) {
              // would like to throw this, but we have crappy stuff in the repo

              fireEvent(ResolutionListener.OMIT_FOR_CYCLE, listeners,
                  new ResolutionNode(e.getArtifact(), childRemoteRepositories, child));
            } catch (ArtifactMetadataRetrievalException e) {
              artifact.setDependencyTrail(node.getDependencyTrail());

              throw new ArtifactResolutionException(
                  "Unable to get dependency information for " + artifact.getId() + ": " + e.getMessage(), artifact,
                  childRemoteRepositories, e);
            }

            ArtifactResolutionRequest subRequest = new ArtifactResolutionRequest(metadataRequest);
            subRequest.setServers(request.getServers());
            subRequest.setMirrors(request.getMirrors());
            subRequest.setProxies(request.getProxies());
            recurse(result, child, resolvedArtifacts, managedVersions, subRequest, source, filter, listeners,
                conflictResolvers);
          }
        } catch (OverConstrainedVersionException e) {
          result.addVersionRangeViolation(e);
        } catch (ArtifactResolutionException e) {
          result.addMetadataResolutionException(e);
        }
      }

      fireEvent(ResolutionListener.FINISH_PROCESSING_CHILDREN, listeners, node);
    }
  }

  private void manageArtifact(ResolutionNode node, ManagedVersionMap managedVersions,
      List<ResolutionListener> listeners) {
    Artifact artifact = managedVersions.get(node.getKey());

    // Before we update the version of the artifact, we need to know
    // whether we are working on a transitive dependency or not. This
    // allows depMgmt to always override transitive dependencies, while
    // explicit child override depMgmt (viz. depMgmt should only
    // provide defaults to children, but should override transitives).
    // We can do this by calling isChildOfRootNode on the current node.

    if ((artifact.getVersion() != null) && (!node.isChildOfRootNode() || node.getArtifact().getVersion() == null)) {
      fireEvent(ResolutionListener.MANAGE_ARTIFACT_VERSION, listeners, node, artifact);
      node.getArtifact().setVersion(artifact.getVersion());
    }

    if ((artifact.getScope() != null) && (!node.isChildOfRootNode() || node.getArtifact().getScope() == null)) {
      fireEvent(ResolutionListener.MANAGE_ARTIFACT_SCOPE, listeners, node, artifact);
      node.getArtifact().setScope(artifact.getScope());
    }

    if (Artifact.SCOPE_SYSTEM.equals(node.getArtifact().getScope()) && (node.getArtifact().getFile() == null)
        && (artifact.getFile() != null)) {
      fireEvent(ResolutionListener.MANAGE_ARTIFACT_SYSTEM_PATH, listeners, node, artifact);
      node.getArtifact().setFile(artifact.getFile());
    }
  }

  /**
   * Check if the artifactScope needs to be updated. <a href=
   * "http://docs.codehaus.org/x/IGU#DependencyMediationandConflictResolution-Scoperesolution">More
   * info</a>.
   *
   * @param farthest farthest resolution node
   * @param nearest nearest resolution node
   * @param listeners
   */
  boolean checkScopeUpdate(ResolutionNode farthest, ResolutionNode nearest, List<ResolutionListener> listeners) {
    boolean updateScope = false;
    Artifact farthestArtifact = farthest.getArtifact();
    Artifact nearestArtifact = nearest.getArtifact();

    /* farthest is runtime and nearest has lower priority, change to runtime */
    if (Artifact.SCOPE_RUNTIME.equals(farthestArtifact.getScope())
        && (Artifact.SCOPE_TEST.equals(nearestArtifact.getScope())
            || Artifact.SCOPE_PROVIDED.equals(nearestArtifact.getScope()))) {
      updateScope = true;
    }

    /* farthest is compile and nearest is not (has lower priority), change to compile */
    if (Artifact.SCOPE_COMPILE.equals(farthestArtifact.getScope())
        && !Artifact.SCOPE_COMPILE.equals(nearestArtifact.getScope())) {
      updateScope = true;
    }

    /* current POM rules all, if nearest is in current pom, do not update its artifactScope */
    if ((nearest.getDepth() < 2) && updateScope) {
      updateScope = false;

      fireEvent(ResolutionListener.UPDATE_SCOPE_CURRENT_POM, listeners, nearest, farthestArtifact);
    }

    if (updateScope) {
      fireEvent(ResolutionListener.UPDATE_SCOPE, listeners, nearest, farthestArtifact);

      // previously we cloned the artifact, but it is more effecient to just update the
      // artifactScope
      // if problems are later discovered that the original object needs its original artifactScope
      // value, cloning
      // may
      // again be appropriate
      nearestArtifact.setScope(farthestArtifact.getScope());
    }

    return updateScope;
  }

  private void fireEvent(int event, List<ResolutionListener> listeners, ResolutionNode node) {
    fireEvent(event, listeners, node, null);
  }

  private void fireEvent(int event, List<ResolutionListener> listeners, ResolutionNode node, Artifact replacement) {
    fireEvent(event, listeners, node, replacement, null);
  }

  private void fireEvent(int event, List<ResolutionListener> listeners, ResolutionNode node, Artifact replacement,
      VersionRange newRange) {
    for (ResolutionListener listener : listeners) {
      switch (event) {
        case ResolutionListener.TEST_ARTIFACT:
          listener.testArtifact(node.getArtifact());
          break;
        case ResolutionListener.PROCESS_CHILDREN:
          listener.startProcessChildren(node.getArtifact());
          break;
        case ResolutionListener.FINISH_PROCESSING_CHILDREN:
          listener.endProcessChildren(node.getArtifact());
          break;
        case ResolutionListener.INCLUDE_ARTIFACT:
          listener.includeArtifact(node.getArtifact());
          break;
        case ResolutionListener.OMIT_FOR_NEARER:
          listener.omitForNearer(node.getArtifact(), replacement);
          break;
        case ResolutionListener.OMIT_FOR_CYCLE:
          listener.omitForCycle(node.getArtifact());
          break;
        case ResolutionListener.UPDATE_SCOPE:
          listener.updateScope(node.getArtifact(), replacement.getScope());
          break;
        case ResolutionListener.UPDATE_SCOPE_CURRENT_POM:
          listener.updateScopeCurrentPom(node.getArtifact(), replacement.getScope());
          break;
        case ResolutionListener.MANAGE_ARTIFACT_VERSION:
          if (listener instanceof ResolutionListenerForDepMgmt) {
            ResolutionListenerForDepMgmt asImpl = (ResolutionListenerForDepMgmt) listener;
            asImpl.manageArtifactVersion(node.getArtifact(), replacement);
          } else {
            listener.manageArtifact(node.getArtifact(), replacement);
          }
          break;
        case ResolutionListener.MANAGE_ARTIFACT_SCOPE:
          if (listener instanceof ResolutionListenerForDepMgmt) {
            ResolutionListenerForDepMgmt asImpl = (ResolutionListenerForDepMgmt) listener;
            asImpl.manageArtifactScope(node.getArtifact(), replacement);
          } else {
            listener.manageArtifact(node.getArtifact(), replacement);
          }
          break;
        case ResolutionListener.MANAGE_ARTIFACT_SYSTEM_PATH:
          if (listener instanceof ResolutionListenerForDepMgmt) {
            ResolutionListenerForDepMgmt asImpl = (ResolutionListenerForDepMgmt) listener;
            asImpl.manageArtifactSystemPath(node.getArtifact(), replacement);
          } else {
            listener.manageArtifact(node.getArtifact(), replacement);
          }
          break;
        case ResolutionListener.SELECT_VERSION_FROM_RANGE:
          listener.selectVersionFromRange(node.getArtifact());
          break;
        case ResolutionListener.RESTRICT_RANGE:
          if (node.getArtifact().getVersionRange().hasRestrictions()
              || replacement.getVersionRange().hasRestrictions()) {
            listener.restrictRange(node.getArtifact(), replacement, newRange);
          }
          break;
        default:
          throw new IllegalStateException("Unknown event: " + event);
      }
    }
  }

  public ArtifactResolutionResult collect(Set<Artifact> artifacts, Artifact originatingArtifact, Map managedVersions,
      ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories, ArtifactMetadataSource source,
      ArtifactFilter filter, List<ResolutionListener> listeners) {
    return collect(artifacts, originatingArtifact, managedVersions, localRepository, remoteRepositories, source, filter,
        listeners, null);
  }

  public ArtifactResolutionResult collect(Set<Artifact> artifacts, Artifact originatingArtifact,
      ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories, ArtifactMetadataSource source,
      ArtifactFilter filter, List<ResolutionListener> listeners) {
    return collect(artifacts, originatingArtifact, null, localRepository, remoteRepositories, source, filter,
        listeners);
  }

}
